{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 192, 192, 3)\n",
      "0 255\n",
      "(3, 6, 192, 192)\n",
      "0.0 1.0\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 576x864 with 6 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "\n",
    "import os,sys\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import helper\n",
    "import simulation\n",
    "\n",
    "# Generate some random images\n",
    "input_images, target_masks = simulation.generate_random_data(192, 192, count=3)\n",
    "\n",
    "for x in [input_images, target_masks]:\n",
    "    print(x.shape)\n",
    "    print(x.min(), x.max())\n",
    "\n",
    "# Change channel-order and make 3 channels for matplot\n",
    "input_images_rgb = [x.astype(np.uint8) for x in input_images]\n",
    "\n",
    "# Map each channel (i.e. class) to each color\n",
    "target_masks_rgb = [helper.masks_to_colorimg(x) for x in target_masks]\n",
    "\n",
    "# Left: Input image, Right: Target mask\n",
    "helper.plot_side_by_side([input_images_rgb, target_masks_rgb])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'train': 2000, 'val': 200}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms, datasets, models\n",
    "\n",
    "class SimDataset(Dataset):\n",
    "    def __init__(self, count, transform=None):\n",
    "        self.input_images, self.target_masks = simulation.generate_random_data(192, 192, count=count)        \n",
    "        self.transform = transform\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self.input_images)\n",
    "    \n",
    "    def __getitem__(self, idx):        \n",
    "        image = self.input_images[idx]\n",
    "        mask = self.target_masks[idx]\n",
    "        if self.transform:\n",
    "            image = self.transform(image)\n",
    "        \n",
    "        return [image, mask]\n",
    "\n",
    "# use same transform for train/val for this example\n",
    "trans = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # imagenet\n",
    "])\n",
    "\n",
    "train_set = SimDataset(2000, transform=trans)\n",
    "val_set = SimDataset(200, transform=trans)\n",
    "\n",
    "image_datasets = {\n",
    "    'train': train_set, 'val': val_set\n",
    "}\n",
    "\n",
    "batch_size = 25\n",
    "\n",
    "dataloaders = {\n",
    "    'train': DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=0),\n",
    "    'val': DataLoader(val_set, batch_size=batch_size, shuffle=True, num_workers=0)\n",
    "}\n",
    "\n",
    "dataset_sizes = {\n",
    "    x: len(image_datasets[x]) for x in image_datasets.keys()\n",
    "}\n",
    "\n",
    "dataset_sizes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([25, 3, 192, 192]) torch.Size([25, 6, 192, 192])\n",
      "-2.117904 2.64 -1.8839339 0.6775894\n",
      "0.0 1.0 0.004708478 0.06845663\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x199ea43cf60>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import torchvision.utils\n",
    "\n",
    "def reverse_transform(inp):\n",
    "    inp = inp.numpy().transpose((1, 2, 0))\n",
    "    mean = np.array([0.485, 0.456, 0.406])\n",
    "    std = np.array([0.229, 0.224, 0.225])\n",
    "    inp = std * inp + mean\n",
    "    inp = np.clip(inp, 0, 1)\n",
    "    inp = (inp * 255).astype(np.uint8)\n",
    "    \n",
    "    return inp\n",
    "\n",
    "# Get a batch of training data\n",
    "inputs, masks = next(iter(dataloaders['train']))\n",
    "\n",
    "print(inputs.shape, masks.shape)\n",
    "for x in [inputs.numpy(), masks.numpy()]:\n",
    "    print(x.min(), x.max(), x.mean(), x.std())\n",
    "\n",
    "plt.imshow(reverse_transform(inputs[3]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False),\n",
       " BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True),\n",
       " ReLU(inplace),\n",
       " MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False),\n",
       " Sequential(\n",
       "   (0): BasicBlock(\n",
       "     (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "   )\n",
       "   (1): BasicBlock(\n",
       "     (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "   )\n",
       " ),\n",
       " Sequential(\n",
       "   (0): BasicBlock(\n",
       "     (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (downsample): Sequential(\n",
       "       (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
       "       (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     )\n",
       "   )\n",
       "   (1): BasicBlock(\n",
       "     (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "   )\n",
       " ),\n",
       " Sequential(\n",
       "   (0): BasicBlock(\n",
       "     (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (downsample): Sequential(\n",
       "       (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
       "       (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     )\n",
       "   )\n",
       "   (1): BasicBlock(\n",
       "     (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "   )\n",
       " ),\n",
       " Sequential(\n",
       "   (0): BasicBlock(\n",
       "     (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (downsample): Sequential(\n",
       "       (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
       "       (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     )\n",
       "   )\n",
       "   (1): BasicBlock(\n",
       "     (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "     (relu): ReLU(inplace)\n",
       "     (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
       "     (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "   )\n",
       " ),\n",
       " AvgPool2d(kernel_size=7, stride=1, padding=0),\n",
       " Linear(in_features=512, out_features=1000, bias=True)]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from torchvision import models\n",
    "\n",
    "base_model = models.resnet18(pretrained=False)\n",
    "    \n",
    "list(base_model.children())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------------------------------\n",
      "        Layer (type)               Output Shape         Param #\n",
      "================================================================\n",
      "            Conv2d-1         [-1, 64, 112, 112]           9,408\n",
      "       BatchNorm2d-2         [-1, 64, 112, 112]             128\n",
      "              ReLU-3         [-1, 64, 112, 112]               0\n",
      "         MaxPool2d-4           [-1, 64, 56, 56]               0\n",
      "            Conv2d-5           [-1, 64, 56, 56]          36,864\n",
      "       BatchNorm2d-6           [-1, 64, 56, 56]             128\n",
      "              ReLU-7           [-1, 64, 56, 56]               0\n",
      "            Conv2d-8           [-1, 64, 56, 56]          36,864\n",
      "       BatchNorm2d-9           [-1, 64, 56, 56]             128\n",
      "             ReLU-10           [-1, 64, 56, 56]               0\n",
      "       BasicBlock-11           [-1, 64, 56, 56]               0\n",
      "           Conv2d-12           [-1, 64, 56, 56]          36,864\n",
      "      BatchNorm2d-13           [-1, 64, 56, 56]             128\n",
      "             ReLU-14           [-1, 64, 56, 56]               0\n",
      "           Conv2d-15           [-1, 64, 56, 56]          36,864\n",
      "      BatchNorm2d-16           [-1, 64, 56, 56]             128\n",
      "             ReLU-17           [-1, 64, 56, 56]               0\n",
      "       BasicBlock-18           [-1, 64, 56, 56]               0\n",
      "           Conv2d-19          [-1, 128, 28, 28]          73,728\n",
      "      BatchNorm2d-20          [-1, 128, 28, 28]             256\n",
      "             ReLU-21          [-1, 128, 28, 28]               0\n",
      "           Conv2d-22          [-1, 128, 28, 28]         147,456\n",
      "      BatchNorm2d-23          [-1, 128, 28, 28]             256\n",
      "           Conv2d-24          [-1, 128, 28, 28]           8,192\n",
      "      BatchNorm2d-25          [-1, 128, 28, 28]             256\n",
      "             ReLU-26          [-1, 128, 28, 28]               0\n",
      "       BasicBlock-27          [-1, 128, 28, 28]               0\n",
      "           Conv2d-28          [-1, 128, 28, 28]         147,456\n",
      "      BatchNorm2d-29          [-1, 128, 28, 28]             256\n",
      "             ReLU-30          [-1, 128, 28, 28]               0\n",
      "           Conv2d-31          [-1, 128, 28, 28]         147,456\n",
      "      BatchNorm2d-32          [-1, 128, 28, 28]             256\n",
      "             ReLU-33          [-1, 128, 28, 28]               0\n",
      "       BasicBlock-34          [-1, 128, 28, 28]               0\n",
      "           Conv2d-35          [-1, 256, 14, 14]         294,912\n",
      "      BatchNorm2d-36          [-1, 256, 14, 14]             512\n",
      "             ReLU-37          [-1, 256, 14, 14]               0\n",
      "           Conv2d-38          [-1, 256, 14, 14]         589,824\n",
      "      BatchNorm2d-39          [-1, 256, 14, 14]             512\n",
      "           Conv2d-40          [-1, 256, 14, 14]          32,768\n",
      "      BatchNorm2d-41          [-1, 256, 14, 14]             512\n",
      "             ReLU-42          [-1, 256, 14, 14]               0\n",
      "       BasicBlock-43          [-1, 256, 14, 14]               0\n",
      "           Conv2d-44          [-1, 256, 14, 14]         589,824\n",
      "      BatchNorm2d-45          [-1, 256, 14, 14]             512\n",
      "             ReLU-46          [-1, 256, 14, 14]               0\n",
      "           Conv2d-47          [-1, 256, 14, 14]         589,824\n",
      "      BatchNorm2d-48          [-1, 256, 14, 14]             512\n",
      "             ReLU-49          [-1, 256, 14, 14]               0\n",
      "       BasicBlock-50          [-1, 256, 14, 14]               0\n",
      "           Conv2d-51            [-1, 512, 7, 7]       1,179,648\n",
      "      BatchNorm2d-52            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-53            [-1, 512, 7, 7]               0\n",
      "           Conv2d-54            [-1, 512, 7, 7]       2,359,296\n",
      "      BatchNorm2d-55            [-1, 512, 7, 7]           1,024\n",
      "           Conv2d-56            [-1, 512, 7, 7]         131,072\n",
      "      BatchNorm2d-57            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-58            [-1, 512, 7, 7]               0\n",
      "       BasicBlock-59            [-1, 512, 7, 7]               0\n",
      "           Conv2d-60            [-1, 512, 7, 7]       2,359,296\n",
      "      BatchNorm2d-61            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-62            [-1, 512, 7, 7]               0\n",
      "           Conv2d-63            [-1, 512, 7, 7]       2,359,296\n",
      "      BatchNorm2d-64            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-65            [-1, 512, 7, 7]               0\n",
      "       BasicBlock-66            [-1, 512, 7, 7]               0\n",
      "        AvgPool2d-67            [-1, 512, 1, 1]               0\n",
      "           Linear-68                 [-1, 1000]         513,000\n",
      "================================================================\n",
      "Total params: 11,689,512\n",
      "Trainable params: 11,689,512\n",
      "Non-trainable params: 0\n",
      "----------------------------------------------------------------\n",
      "Input size (MB): 0.57\n",
      "Forward/backward pass size (MB): 62.79\n",
      "Params size (MB): 44.59\n",
      "Estimated Total Size (MB): 107.96\n",
      "----------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "# check keras-like model summary using torchsummary\n",
    "import torch\n",
    "from torchsummary import summary\n",
    "\n",
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "base_model = base_model.to(device)\n",
    "\n",
    "summary(base_model, input_size=(3, 224, 224))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "\n",
    "def convrelu(in_channels, out_channels, kernel, padding):\n",
    "    return nn.Sequential(\n",
    "        nn.Conv2d(in_channels, out_channels, kernel, padding=padding),\n",
    "        nn.ReLU(inplace=True),\n",
    "    )\n",
    "\n",
    "class ResNetUNet(nn.Module):\n",
    "\n",
    "    def __init__(self, n_class):\n",
    "        super().__init__()\n",
    "        \n",
    "        self.base_model = models.resnet18(pretrained=True)\n",
    "        \n",
    "        self.base_layers = list(base_model.children())                \n",
    "        \n",
    "        self.layer0 = nn.Sequential(*self.base_layers[:3]) # size=(N, 64, x.H/2, x.W/2)\n",
    "        self.layer0_1x1 = convrelu(64, 64, 1, 0)\n",
    "        self.layer1 = nn.Sequential(*self.base_layers[3:5]) # size=(N, 64, x.H/4, x.W/4)        \n",
    "        self.layer1_1x1 = convrelu(64, 64, 1, 0)       \n",
    "        self.layer2 = self.base_layers[5]  # size=(N, 128, x.H/8, x.W/8)        \n",
    "        self.layer2_1x1 = convrelu(128, 128, 1, 0)  \n",
    "        self.layer3 = self.base_layers[6]  # size=(N, 256, x.H/16, x.W/16)        \n",
    "        self.layer3_1x1 = convrelu(256, 256, 1, 0)  \n",
    "        self.layer4 = self.base_layers[7]  # size=(N, 512, x.H/32, x.W/32)\n",
    "        self.layer4_1x1 = convrelu(512, 512, 1, 0)  \n",
    "        \n",
    "        self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n",
    "        \n",
    "        self.conv_up3 = convrelu(256 + 512, 512, 3, 1)\n",
    "        self.conv_up2 = convrelu(128 + 512, 256, 3, 1)\n",
    "        self.conv_up1 = convrelu(64 + 256, 256, 3, 1)\n",
    "        self.conv_up0 = convrelu(64 + 256, 128, 3, 1)\n",
    "        \n",
    "        self.conv_original_size0 = convrelu(3, 64, 3, 1)\n",
    "        self.conv_original_size1 = convrelu(64, 64, 3, 1)\n",
    "        self.conv_original_size2 = convrelu(64 + 128, 64, 3, 1)\n",
    "        \n",
    "        self.conv_last = nn.Conv2d(64, n_class, 1)\n",
    "        \n",
    "    def forward(self, input):\n",
    "        x_original = self.conv_original_size0(input)\n",
    "        x_original = self.conv_original_size1(x_original)\n",
    "        \n",
    "        layer0 = self.layer0(input)            \n",
    "        layer1 = self.layer1(layer0)\n",
    "        layer2 = self.layer2(layer1)\n",
    "        layer3 = self.layer3(layer2)        \n",
    "        layer4 = self.layer4(layer3)\n",
    "        \n",
    "        layer4 = self.layer4_1x1(layer4)\n",
    "        x = self.upsample(layer4)\n",
    "        layer3 = self.layer3_1x1(layer3)\n",
    "        x = torch.cat([x, layer3], dim=1)\n",
    "        x = self.conv_up3(x)\n",
    " \n",
    "        x = self.upsample(x)\n",
    "        layer2 = self.layer2_1x1(layer2)\n",
    "        x = torch.cat([x, layer2], dim=1)\n",
    "        x = self.conv_up2(x)\n",
    "\n",
    "        x = self.upsample(x)\n",
    "        layer1 = self.layer1_1x1(layer1)\n",
    "        x = torch.cat([x, layer1], dim=1)\n",
    "        x = self.conv_up1(x)\n",
    "\n",
    "        x = self.upsample(x)\n",
    "        layer0 = self.layer0_1x1(layer0)\n",
    "        x = torch.cat([x, layer0], dim=1)\n",
    "        x = self.conv_up0(x)\n",
    "        \n",
    "        x = self.upsample(x)\n",
    "        x = torch.cat([x, x_original], dim=1)\n",
    "        x = self.conv_original_size2(x)        \n",
    "        \n",
    "        out = self.conv_last(x)        \n",
    "        \n",
    "        return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------------------------------\n",
      "        Layer (type)               Output Shape         Param #\n",
      "================================================================\n",
      "            Conv2d-1         [-1, 64, 224, 224]           1,792\n",
      "              ReLU-2         [-1, 64, 224, 224]               0\n",
      "            Conv2d-3         [-1, 64, 224, 224]          36,928\n",
      "              ReLU-4         [-1, 64, 224, 224]               0\n",
      "            Conv2d-5         [-1, 64, 112, 112]           9,408\n",
      "       BatchNorm2d-6         [-1, 64, 112, 112]             128\n",
      "              ReLU-7         [-1, 64, 112, 112]               0\n",
      "         MaxPool2d-8           [-1, 64, 56, 56]               0\n",
      "            Conv2d-9           [-1, 64, 56, 56]          36,864\n",
      "      BatchNorm2d-10           [-1, 64, 56, 56]             128\n",
      "             ReLU-11           [-1, 64, 56, 56]               0\n",
      "           Conv2d-12           [-1, 64, 56, 56]          36,864\n",
      "      BatchNorm2d-13           [-1, 64, 56, 56]             128\n",
      "             ReLU-14           [-1, 64, 56, 56]               0\n",
      "       BasicBlock-15           [-1, 64, 56, 56]               0\n",
      "           Conv2d-16           [-1, 64, 56, 56]          36,864\n",
      "      BatchNorm2d-17           [-1, 64, 56, 56]             128\n",
      "             ReLU-18           [-1, 64, 56, 56]               0\n",
      "           Conv2d-19           [-1, 64, 56, 56]          36,864\n",
      "      BatchNorm2d-20           [-1, 64, 56, 56]             128\n",
      "             ReLU-21           [-1, 64, 56, 56]               0\n",
      "       BasicBlock-22           [-1, 64, 56, 56]               0\n",
      "           Conv2d-23          [-1, 128, 28, 28]          73,728\n",
      "      BatchNorm2d-24          [-1, 128, 28, 28]             256\n",
      "             ReLU-25          [-1, 128, 28, 28]               0\n",
      "           Conv2d-26          [-1, 128, 28, 28]         147,456\n",
      "      BatchNorm2d-27          [-1, 128, 28, 28]             256\n",
      "           Conv2d-28          [-1, 128, 28, 28]           8,192\n",
      "      BatchNorm2d-29          [-1, 128, 28, 28]             256\n",
      "             ReLU-30          [-1, 128, 28, 28]               0\n",
      "       BasicBlock-31          [-1, 128, 28, 28]               0\n",
      "           Conv2d-32          [-1, 128, 28, 28]         147,456\n",
      "      BatchNorm2d-33          [-1, 128, 28, 28]             256\n",
      "             ReLU-34          [-1, 128, 28, 28]               0\n",
      "           Conv2d-35          [-1, 128, 28, 28]         147,456\n",
      "      BatchNorm2d-36          [-1, 128, 28, 28]             256\n",
      "             ReLU-37          [-1, 128, 28, 28]               0\n",
      "       BasicBlock-38          [-1, 128, 28, 28]               0\n",
      "           Conv2d-39          [-1, 256, 14, 14]         294,912\n",
      "      BatchNorm2d-40          [-1, 256, 14, 14]             512\n",
      "             ReLU-41          [-1, 256, 14, 14]               0\n",
      "           Conv2d-42          [-1, 256, 14, 14]         589,824\n",
      "      BatchNorm2d-43          [-1, 256, 14, 14]             512\n",
      "           Conv2d-44          [-1, 256, 14, 14]          32,768\n",
      "      BatchNorm2d-45          [-1, 256, 14, 14]             512\n",
      "             ReLU-46          [-1, 256, 14, 14]               0\n",
      "       BasicBlock-47          [-1, 256, 14, 14]               0\n",
      "           Conv2d-48          [-1, 256, 14, 14]         589,824\n",
      "      BatchNorm2d-49          [-1, 256, 14, 14]             512\n",
      "             ReLU-50          [-1, 256, 14, 14]               0\n",
      "           Conv2d-51          [-1, 256, 14, 14]         589,824\n",
      "      BatchNorm2d-52          [-1, 256, 14, 14]             512\n",
      "             ReLU-53          [-1, 256, 14, 14]               0\n",
      "       BasicBlock-54          [-1, 256, 14, 14]               0\n",
      "           Conv2d-55            [-1, 512, 7, 7]       1,179,648\n",
      "      BatchNorm2d-56            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-57            [-1, 512, 7, 7]               0\n",
      "           Conv2d-58            [-1, 512, 7, 7]       2,359,296\n",
      "      BatchNorm2d-59            [-1, 512, 7, 7]           1,024\n",
      "           Conv2d-60            [-1, 512, 7, 7]         131,072\n",
      "      BatchNorm2d-61            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-62            [-1, 512, 7, 7]               0\n",
      "       BasicBlock-63            [-1, 512, 7, 7]               0\n",
      "           Conv2d-64            [-1, 512, 7, 7]       2,359,296\n",
      "      BatchNorm2d-65            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-66            [-1, 512, 7, 7]               0\n",
      "           Conv2d-67            [-1, 512, 7, 7]       2,359,296\n",
      "      BatchNorm2d-68            [-1, 512, 7, 7]           1,024\n",
      "             ReLU-69            [-1, 512, 7, 7]               0\n",
      "       BasicBlock-70            [-1, 512, 7, 7]               0\n",
      "           Conv2d-71            [-1, 512, 7, 7]         262,656\n",
      "             ReLU-72            [-1, 512, 7, 7]               0\n",
      "         Upsample-73          [-1, 512, 14, 14]               0\n",
      "           Conv2d-74          [-1, 256, 14, 14]          65,792\n",
      "             ReLU-75          [-1, 256, 14, 14]               0\n",
      "           Conv2d-76          [-1, 512, 14, 14]       3,539,456\n",
      "             ReLU-77          [-1, 512, 14, 14]               0\n",
      "         Upsample-78          [-1, 512, 28, 28]               0\n",
      "           Conv2d-79          [-1, 128, 28, 28]          16,512\n",
      "             ReLU-80          [-1, 128, 28, 28]               0\n",
      "           Conv2d-81          [-1, 256, 28, 28]       1,474,816\n",
      "             ReLU-82          [-1, 256, 28, 28]               0\n",
      "         Upsample-83          [-1, 256, 56, 56]               0\n",
      "           Conv2d-84           [-1, 64, 56, 56]           4,160\n",
      "             ReLU-85           [-1, 64, 56, 56]               0\n",
      "           Conv2d-86          [-1, 256, 56, 56]         737,536\n",
      "             ReLU-87          [-1, 256, 56, 56]               0\n",
      "         Upsample-88        [-1, 256, 112, 112]               0\n",
      "           Conv2d-89         [-1, 64, 112, 112]           4,160\n",
      "             ReLU-90         [-1, 64, 112, 112]               0\n",
      "           Conv2d-91        [-1, 128, 112, 112]         368,768\n",
      "             ReLU-92        [-1, 128, 112, 112]               0\n",
      "         Upsample-93        [-1, 128, 224, 224]               0\n",
      "           Conv2d-94         [-1, 64, 224, 224]         110,656\n",
      "             ReLU-95         [-1, 64, 224, 224]               0\n",
      "           Conv2d-96          [-1, 6, 224, 224]             390\n",
      "================================================================\n",
      "Total params: 17,800,134\n",
      "Trainable params: 17,800,134\n",
      "Non-trainable params: 0\n",
      "----------------------------------------------------------------\n",
      "Input size (MB): 0.57\n",
      "Forward/backward pass size (MB): 354.87\n",
      "Params size (MB): 67.90\n",
      "Estimated Total Size (MB): 423.34\n",
      "----------------------------------------------------------------\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\naotous\\AppData\\Local\\Continuum\\anaconda3\\envs\\torch\\lib\\site-packages\\torch\\nn\\modules\\upsampling.py:129: UserWarning: nn.Upsample is deprecated. Use nn.functional.interpolate instead.\n",
      "  warnings.warn(\"nn.{} is deprecated. Use nn.functional.interpolate instead.\".format(self.name))\n"
     ]
    }
   ],
   "source": [
    "# check keras-like model summary using torchsummary\n",
    "\n",
    "from torchsummary import summary\n",
    "\n",
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "model = ResNetUNet(6)\n",
    "model = model.to(device)\n",
    "\n",
    "summary(model, input_size=(3, 224, 224))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import defaultdict\n",
    "import torch.nn.functional as F\n",
    "import torch\n",
    "from loss import dice_loss\n",
    "\n",
    "def calc_loss(pred, target, metrics, bce_weight=0.5):\n",
    "    bce = F.binary_cross_entropy_with_logits(pred, target)\n",
    "        \n",
    "    pred = torch.sigmoid(pred)\n",
    "    dice = dice_loss(pred, target)\n",
    "    \n",
    "    loss = bce * bce_weight + dice * (1 - bce_weight)\n",
    "    \n",
    "    metrics['bce'] += bce.data.cpu().numpy() * target.size(0)\n",
    "    metrics['dice'] += dice.data.cpu().numpy() * target.size(0)\n",
    "    metrics['loss'] += loss.data.cpu().numpy() * target.size(0)\n",
    "    \n",
    "    return loss\n",
    "\n",
    "def print_metrics(metrics, epoch_samples, phase):    \n",
    "    outputs = []\n",
    "    for k in metrics.keys():\n",
    "        outputs.append(\"{}: {:4f}\".format(k, metrics[k] / epoch_samples))\n",
    "        \n",
    "    print(\"{}: {}\".format(phase, \", \".join(outputs)))    \n",
    "\n",
    "def train_model(model, optimizer, scheduler, num_epochs=25):\n",
    "    best_model_wts = copy.deepcopy(model.state_dict())\n",
    "    best_loss = 1e10\n",
    "\n",
    "    for epoch in range(num_epochs):\n",
    "        print('Epoch {}/{}'.format(epoch, num_epochs - 1))\n",
    "        print('-' * 10)\n",
    "        \n",
    "        since = time.time()\n",
    "\n",
    "        # Each epoch has a training and validation phase\n",
    "        for phase in ['train', 'val']:\n",
    "            if phase == 'train':\n",
    "                scheduler.step()\n",
    "                for param_group in optimizer.param_groups:\n",
    "                    print(\"LR\", param_group['lr'])\n",
    "                    \n",
    "                model.train()  # Set model to training mode\n",
    "            else:\n",
    "                model.eval()   # Set model to evaluate mode\n",
    "\n",
    "            metrics = defaultdict(float)\n",
    "            epoch_samples = 0\n",
    "            \n",
    "            for inputs, labels in dataloaders[phase]:\n",
    "                inputs = inputs.to(device)\n",
    "                labels = labels.to(device)             \n",
    "\n",
    "                # zero the parameter gradients\n",
    "                optimizer.zero_grad()\n",
    "\n",
    "                # forward\n",
    "                # track history if only in train\n",
    "                with torch.set_grad_enabled(phase == 'train'):\n",
    "                    outputs = model(inputs)\n",
    "                    loss = calc_loss(outputs, labels, metrics)\n",
    "\n",
    "                    # backward + optimize only if in training phase\n",
    "                    if phase == 'train':\n",
    "                        loss.backward()\n",
    "                        optimizer.step()\n",
    "\n",
    "                # statistics\n",
    "                epoch_samples += inputs.size(0)\n",
    "\n",
    "            print_metrics(metrics, epoch_samples, phase)\n",
    "            epoch_loss = metrics['loss'] / epoch_samples\n",
    "\n",
    "            # deep copy the model\n",
    "            if phase == 'val' and epoch_loss < best_loss:\n",
    "                print(\"saving best model\")\n",
    "                best_loss = epoch_loss\n",
    "                best_model_wts = copy.deepcopy(model.state_dict())\n",
    "\n",
    "        time_elapsed = time.time() - since\n",
    "        print('{:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))\n",
    "            \n",
    "    print('Best val loss: {:4f}'.format(best_loss))\n",
    "\n",
    "    # load best model weights\n",
    "    model.load_state_dict(best_model_wts)\n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cuda:0\n",
      "Epoch 0/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.106049, dice: 0.939754, loss: 0.522902\n",
      "val: bce: 0.021187, dice: 0.740403, loss: 0.380795\n",
      "saving best model\n",
      "1m 13s\n",
      "Epoch 1/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.021152, dice: 0.539552, loss: 0.280352\n",
      "val: bce: 0.015782, dice: 0.403593, loss: 0.209687\n",
      "saving best model\n",
      "1m 23s\n",
      "Epoch 2/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.011438, dice: 0.297177, loss: 0.154307\n",
      "val: bce: 0.008063, dice: 0.226069, loss: 0.117066\n",
      "saving best model\n",
      "1m 35s\n",
      "Epoch 3/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.007809, dice: 0.202010, loss: 0.104910\n",
      "val: bce: 0.007454, dice: 0.183544, loss: 0.095499\n",
      "saving best model\n",
      "1m 40s\n",
      "Epoch 4/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.006656, dice: 0.175859, loss: 0.091257\n",
      "val: bce: 0.006215, dice: 0.169616, loss: 0.087916\n",
      "saving best model\n",
      "1m 39s\n",
      "Epoch 5/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.004646, dice: 0.149397, loss: 0.077021\n",
      "val: bce: 0.003372, dice: 0.134775, loss: 0.069073\n",
      "saving best model\n",
      "1m 39s\n",
      "Epoch 6/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.002749, dice: 0.107913, loss: 0.055331\n",
      "val: bce: 0.002363, dice: 0.093581, loss: 0.047972\n",
      "saving best model\n",
      "1m 40s\n",
      "Epoch 7/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.002137, dice: 0.079275, loss: 0.040706\n",
      "val: bce: 0.002504, dice: 0.086800, loss: 0.044652\n",
      "saving best model\n",
      "1m 39s\n",
      "Epoch 8/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.001844, dice: 0.062099, loss: 0.031971\n",
      "val: bce: 0.002087, dice: 0.069803, loss: 0.035945\n",
      "saving best model\n",
      "1m 40s\n",
      "Epoch 9/14\n",
      "----------\n",
      "LR 0.0001\n",
      "train: bce: 0.001642, dice: 0.050935, loss: 0.026289\n",
      "val: bce: 0.001931, dice: 0.064805, loss: 0.033368\n",
      "saving best model\n",
      "1m 40s\n",
      "Epoch 10/14\n",
      "----------\n",
      "LR 1e-05\n",
      "train: bce: 0.001492, dice: 0.043337, loss: 0.022415\n",
      "val: bce: 0.001986, dice: 0.063748, loss: 0.032867\n",
      "saving best model\n",
      "1m 41s\n",
      "Epoch 11/14\n",
      "----------\n",
      "LR 1e-05\n",
      "train: bce: 0.001475, dice: 0.042005, loss: 0.021740\n",
      "val: bce: 0.002009, dice: 0.063995, loss: 0.033002\n",
      "1m 54s\n",
      "Epoch 12/14\n",
      "----------\n",
      "LR 1e-05\n",
      "train: bce: 0.001460, dice: 0.041238, loss: 0.021349\n",
      "val: bce: 0.002017, dice: 0.064332, loss: 0.033175\n",
      "1m 55s\n",
      "Epoch 13/14\n",
      "----------\n",
      "LR 1e-05\n",
      "train: bce: 0.001450, dice: 0.040528, loss: 0.020989\n",
      "val: bce: 0.002030, dice: 0.064902, loss: 0.033466\n",
      "1m 54s\n",
      "Epoch 14/14\n",
      "----------\n",
      "LR 1e-05\n",
      "train: bce: 0.001433, dice: 0.039867, loss: 0.020650\n",
      "val: bce: 0.002121, dice: 0.066033, loss: 0.034077\n",
      "1m 54s\n",
      "Best val loss: 0.032867\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.optim as optim\n",
    "from torch.optim import lr_scheduler\n",
    "import time\n",
    "import copy\n",
    "\n",
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "print(device)\n",
    "\n",
    "num_class = 6\n",
    "\n",
    "model = ResNetUNet(num_class).to(device)\n",
    "\n",
    "# freeze backbone layers\n",
    "# Comment out to finetune further\n",
    "for l in model.base_layers:\n",
    "    for param in l.parameters():\n",
    "        param.requires_grad = False\n",
    "\n",
    "optimizer_ft = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4)\n",
    "\n",
    "exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=10, gamma=0.1)        \n",
    "        \n",
    "model = train_model(model, optimizer_ft, exp_lr_scheduler, num_epochs=15)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 6, 192, 192)\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x864 with 9 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#### prediction\n",
    "\n",
    "import math\n",
    "\n",
    "model.eval()   # Set model to evaluate mode\n",
    "\n",
    "test_dataset = SimDataset(3, transform = trans)\n",
    "test_loader = DataLoader(test_dataset, batch_size=3, shuffle=False, num_workers=0)\n",
    "        \n",
    "inputs, labels = next(iter(test_loader))\n",
    "inputs = inputs.to(device)\n",
    "labels = labels.to(device)\n",
    "\n",
    "pred = model(inputs)\n",
    "pred = torch.sigmoid(pred)\n",
    "pred = pred.data.cpu().numpy()\n",
    "print(pred.shape)\n",
    "\n",
    "# Change channel-order and make 3 channels for matplot\n",
    "input_images_rgb = [reverse_transform(x) for x in inputs.cpu()]\n",
    "\n",
    "# Map each channel (i.e. class) to each color\n",
    "target_masks_rgb = [helper.masks_to_colorimg(x) for x in labels.cpu().numpy()]\n",
    "pred_rgb = [helper.masks_to_colorimg(x) for x in pred]\n",
    "\n",
    "helper.plot_side_by_side([input_images_rgb, target_masks_rgb, pred_rgb])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
